#include "vl_c_dep_support.h"
#include "vl_c_config_support.h"
#include "vl_c_compile_support.h"
#include "utils/vl_cmd.h"

using namespace VL;

/**
 * @Author: CimZzz
 * @Date: 2022-08-13 16:07:02
 * 
 * 构造函数
 */
CDepSupport::CDepSupport(): VLSupport(VLSupport::Aspect::Dependency) {

}


/**
 * @Author: CimZzz
 * @Date: 2022-08-19 15:53:54
 * 
 * 执行动作
 * 
 * @param env 环境变量
 */
void CDepSupport::doAction(VLEnv* env) {
	// 加载 C / C++ 项目工程配置文件, 转换为内存中的数据类型
	this->initProjectMustExist(env);

	auto projectProp = env->projectProperties;
	auto dependencyParams = env->dependencyParams;

	// 检查 env 中是否已经存在对应的 Dirty 信息
	// 如果存在, 则直接跳过本次 Dirty 检查
	auto depBundlePtr = env->getVariable<CDepBundle>("C_DEP_BUNDLE");
	
	if(depBundlePtr.get() != nullptr && (dependencyParams->depOptions & DependencyParams::DepOptions::DO_ForceCheckDep) == 0) {
		// 跳过读取 Dirty 阶段, 直接到打印阶段
		if(dependencyParams->needPrint) {
			this->printConfig(depBundlePtr);
		}
		return;
	}
	depBundlePtr = std::shared_ptr<CDepBundle>(new CDepBundle());

	VLFile remoteDepFile(projectProp->propDir, "remote_file");
	if((dependencyParams->depOptions & DependencyParams::DepOptions::DO_ForceCheckDep) == 0) {
		if(remoteDepFile.isExist()) {
			// 存在 Dep 文件, 直接读取
			RootProp depProp;
			if(depProp.readFromVLCFile(remoteDepFile)) {
				// 读取成功, 进行解析
				for(auto depIter = depProp.begin() ; depIter != depProp.end() ; depIter ++) {
					std::shared_ptr<CDepData> depDataPtr(new CDepData);
					auto depDataPropPtr = depIter->second;

					// Dep 数据节点必须为 Map 类型
					if(!depDataPropPtr->isMap()) {
						this->throwException("analyze dep file failed: %s", remoteDepFile.getAbsolutePathStr());
					}
					auto depDataMapPropPtr = (MapProp *) depDataPropPtr.get();

					depDataPtr->LDFLAGS = VLFlag(depDataMapPropPtr->get("LDFLAGS"), " ");
					depDataPtr->includeDirPtr = std::shared_ptr<VLFile>(new VLAbsolutePathFile(depDataMapPropPtr->get("INCLUDE_DIR")->getValue()));
					depDataPtr->libDirPtr = std::shared_ptr<VLFile>(new VLAbsolutePathFile(depDataMapPropPtr->get("LIB_DIR")->getValue()));

					auto dynamicFilePropPtr = depDataMapPropPtr->get("DYM_LIB");
					auto staticFilePropPtr = depDataMapPropPtr->get("STATIC_LIB");
					auto nopStaticFilePropPtr = depDataMapPropPtr->get("NOP_STATIC_LIB");
					if(dynamicFilePropPtr->isNode()) {
						std::shared_ptr<VLFile> dynamicFilePtr(new VLAbsolutePathFile(dynamicFilePropPtr->getValue()));
						if(dynamicFilePtr->isExist()) {
							depDataPtr->dynamicFilePtr = dynamicFilePtr;
						}
					}
					if(staticFilePropPtr->isNode()) {
						std::shared_ptr<VLFile> staticFilePtr(new VLAbsolutePathFile(staticFilePropPtr->getValue()));
						if(staticFilePtr->isExist()) {
							depDataPtr->staticFilePtr = staticFilePtr;
						}
					}
					if(nopStaticFilePropPtr->isNode()) {
						std::shared_ptr<VLFile> nopStaticFilePtr(new VLAbsolutePathFile(nopStaticFilePropPtr->getValue()));
						if(nopStaticFilePtr->isExist()) {
							depDataPtr->nopStaticFilePtr = nopStaticFilePtr;
						}
					}

					if(depBundlePtr->depMap.get() == nullptr) {
						depBundlePtr->depMap = std::shared_ptr<std::unordered_map<std::string, std::shared_ptr<CDepData>>>(new std::unordered_map<std::string, std::shared_ptr<CDepData>>);
					}
					depBundlePtr->depMap->emplace(depIter->first, depDataPtr);
				}
				env->saveVariable<CDepBundle>("C_DEP_BUNDLE", depBundlePtr);

				if(dependencyParams->needPrint) {
					this->printConfig(depBundlePtr);
				}
				return;
			}
		}
	}

	if(dependencyParams->depOptions == DependencyParams::DepOptions::DO_OnlyCheckDep) {
		// 仅加载属性文件, 不进行后续解析
		return;
	}
	// 获取 ConfigBundle, 仅解析依赖项
	CConfigSupport configSupport;
	env->configureParams = std::shared_ptr<ConfigureParams>(new ConfigureParams);
	env->configureParams->needPrint = false;
	env->configureParams->configOptions = ConfigureParams::ConfigOptions::CO_ReadDependencies;
	configSupport.doAction(env);
	auto configBundle = env->getVariable<CConfigBundle>("C_CONFIG_BUNDLE");

	// 初始化线程池
	env->initThreadPool();

	auto remoteDependenciesPtr = configBundle->remoteDependenciesPtr;
	if(remoteDependenciesPtr.get() != nullptr) {
		RootProp depProp;
		Buffer cmdBuffer;

		// 下载依赖库文件
		for(auto depIter = remoteDependenciesPtr->begin() ; depIter != remoteDependenciesPtr->end() ; depIter ++) {
			auto depPtr = (*depIter);
			
			// 设置依赖库文件夹
			VLFile depDir(projectProp->outSourceDir, depPtr->name);

			bool isPullSuccess = false;

			if(depDir.isExist()) {
				// 如果文件夹存在, 先尝试 git pull 更新
				// 如果 git pull 失败, 删掉依赖库文件重新下载

				cmdBuffer.reset();
				cmdBuffer.appendPrint("git -C %s checkout %s", depDir.getAbsolutePathStr(), depPtr->extraInfo.c_str());
				cmdBuffer.appendByte('\0');
				printf("%s\n", cmdBuffer.getBufferAsChar());

				if(!CMDExecute::execute(cmdBuffer.getBufferAsChar())) {
					// git checkout 失败

					cmdBuffer.reset();
					cmdBuffer.appendPrint("git -C %s pull", depDir.getAbsolutePathStr());
					cmdBuffer.appendByte('\0');
					printf("%s\n", cmdBuffer.getBufferAsChar());

					if(!CMDExecute::execute(cmdBuffer.getBufferAsChar())) {
						// git pull 成功
						isPullSuccess = true;
					} else {
						if(!depDir.deleteFile()) {
							this->throwException("delete dependency failed: %s", depDir.getAbsolutePathStr());
						}
					}
				} else {
					// git checkout 失败
					if(!depDir.deleteFile()) {
						this->throwException("delete dependency failed: %s", depDir.getAbsolutePathStr());
					}
				}
			}

			if(!isPullSuccess) {
				// 重新克隆一份代码
				cmdBuffer.reset();
				cmdBuffer.appendPrint("git clone -b \"%s\" \"%s\" \"%s\"", depPtr->extraInfo.c_str(), depPtr->path.c_str(), depDir.getAbsolutePathStr());
				cmdBuffer.appendByte('\0');
				printf("%s\n", cmdBuffer.getBufferAsChar());
				if(CMDExecute::execute(cmdBuffer.getBufferAsChar())) {
					// git clone 失败
					this->throwException("clone dependency failed: %s", depPtr->name.c_str());
				}
			}

			auto installDir = std::shared_ptr<VLFile>(new VLFile(projectProp->outProductDir));
			// 编译依赖库
			VLEnv newEnv;
			newEnv.projectDir = std::shared_ptr<VLFile>(new VLFile(depDir));
			CCompileSupport support;
			newEnv.commonParams->toolchainFile = env->commonParams->toolchainFile;
			newEnv.commonParams->installDir = installDir;
			newEnv.compileParam = std::shared_ptr<CompileParams>(new CompileParams);
			newEnv.compileParam->checkDep = true;
			newEnv.compileParam->compileOption = CompileParams::CompileOptions::CO_LIB;
			support.doAction(&newEnv);

			// 依赖库编译成功, 加载依赖库的 LDFLAGS
			auto depConfigBundle = newEnv.getVariable<CConfigBundle>("C_CONFIG_BUNDLE");
			if(depConfigBundle.get() == nullptr) {
				// 配置过程中失败
				this->throwException("compile dependency failed: %s", depPtr->name.c_str());
			}
			auto libBundlesPtr = depConfigBundle->libBundlesPtr;
			if(libBundlesPtr.get() != nullptr) {
				for(auto libIter = libBundlesPtr->begin() ; libIter != libBundlesPtr->end() ; libIter ++) {
					auto libBundlePtr = (*libIter);
					auto depDataPtr = std::shared_ptr<CDepData>(new CDepData);
					VLFile rootDir(*installDir, "lib");
					VLFile libDir(rootDir, libBundlePtr->name);
					
					std::shared_ptr<VLFile> libIncludeDirPtr(new VLFile(libDir, "include"));
					std::shared_ptr<VLFile> libLibDirPtr(new VLFile(libDir, "lib"));
					depDataPtr->includeDirPtr = libIncludeDirPtr;
					depDataPtr->libDirPtr = libLibDirPtr;
					depDataPtr->LDFLAGS = libBundlePtr->LDFLAGS;
					
					auto libMapPropPtr = std::shared_ptr<MapProp>(new MapProp);
					libMapPropPtr->push("LDFLAGS", libBundlePtr->LDFLAGS.asProp());
					libMapPropPtr->push("INCLUDE_DIR", libIncludeDirPtr->getAbsolutePathStr());
					libMapPropPtr->push("LIB_DIR", libLibDirPtr->getAbsolutePathStr());
					if(libBundlePtr->dynamicFilePtr.get() != nullptr) {
						depDataPtr->dynamicFilePtr = std::shared_ptr<VLFile>(libBundlePtr->dynamicFilePtr->getFilePtr());
						libMapPropPtr->push("DYM_LIB", libBundlePtr->dynamicFilePtr->getAbsolutePathStr());
					}
					if(libBundlePtr->staticFilePtr.get() != nullptr) {
						depDataPtr->staticFilePtr = std::shared_ptr<VLFile>(libBundlePtr->staticFilePtr->getFilePtr());
						libMapPropPtr->push("STATIC_LIB", libBundlePtr->staticFilePtr->getAbsolutePathStr());
					}
					if(libBundlePtr->nopStaticFilePtr.get() != nullptr) {
						depDataPtr->nopStaticFilePtr = std::shared_ptr<VLFile>(libBundlePtr->nopStaticFilePtr->getFilePtr());
						libMapPropPtr->push("NOP_STATIC_LIB", libBundlePtr->nopStaticFilePtr->getAbsolutePathStr());
					}

					if(depBundlePtr->depMap.get() == nullptr) {
						depBundlePtr->depMap = std::shared_ptr<std::unordered_map<std::string, std::shared_ptr<CDepData>>>(new std::unordered_map<std::string, std::shared_ptr<CDepData>>);
					}
					depBundlePtr->depMap->emplace(libBundlePtr->name, depDataPtr);
					depProp(libBundlePtr->name, libMapPropPtr);
				}
			}
		}
		
		depProp.writeToVLCFile(remoteDepFile);
		env->saveVariable<CDepBundle>("C_DEP_BUNDLE", depBundlePtr);
	} else {
		// 删除远程依赖文件
		remoteDepFile.deleteFile();
	}

	if(dependencyParams->needPrint) {
		this->printConfig(depBundlePtr);
	}
}

/**
 * @Author: CimZzz
 * @Date: 2022-08-22 23:57:31
 * 
 * 打印依赖数据信息
 * 
 * @param bundlePtr 依赖数据指针
 */
void CDepSupport::printConfig(const std::shared_ptr<CDepBundle>& bundlePtr) {
	printf("=============== Dependencies Info ===============\n");
	auto depMap = bundlePtr->depMap;
	if(depMap.get() != nullptr) {
		// 存在依赖数据
		printf("Dependencies Options: \n");
		for(auto depIter = depMap->begin() ; depIter != depMap->end() ; depIter ++) {
			auto dependency = depIter->second;
			printf("-----\n");
			printf("%-18s: %s\n", "NAME", depIter->first.c_str());
			printf("%-18s: %s\n", "LDFLAGS", dependency->LDFLAGS.c_str());
			printf("%-18s: %s\n", "INCLUDE DIR:", dependency->includeDirPtr->getAbsolutePathStr());
			printf("%-18s: %s\n", "LIB DIR", dependency->libDirPtr->getAbsolutePathStr());

			if(dependency->dynamicFilePtr.get() != nullptr) {
				printf("%-18s: %s\n", "DYNAMIC LIB", dependency->dynamicFilePtr->getAbsolutePathStr());
			}

			if(dependency->staticFilePtr.get() != nullptr) {
				printf("%-18s: %s\n", "STATIC LIB", dependency->staticFilePtr->getAbsolutePathStr());
			}

			if(dependency->nopStaticFilePtr.get() != nullptr) {
				printf("%-18s: %s\n", "NOP STATIC LIB", dependency->nopStaticFilePtr->getAbsolutePathStr());
			}
		}
	}
}